--------------------------------------------------------
--
-- Link
-- 
-- Communication link between two processors
--
--------------------------------------------------------

-- adding comment here to check for a commit

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity link is

  port ( clk             : in  std_logic;
         enable          : in  std_logic;
         initialize      : in  std_logic;
         reset_in        : in  std_logic;
         link_data_in    : in  std_logic;
         h_target_label  : in  std_logic_vector(23 DownTo 0);
         h_name          : in  std_logic_vector(23 DownTo 0);
         dir             : in  integer;
         reset_out       : out std_logic;
         link_data_out   : out std_logic;
         link_killed     : out std_logic;
         b_position      : out integer;
         b_label_current : out std_logic_vector(23 DownTo 0);
         link_complete   : out std_logic
       );
         
end link;

architecture bhv of link is

  type clock_state_t is (state0, state1, state2, state3, state4, state5, state6, state7, state8);
  type link_state_t is (SEND, RECEIVE, COMPLETE, RESET, DISCONNECTED, KILLED);
  
  signal build_label : std_logic_vector (23 DownTo 0);
  signal target_label : std_logic_vector (23 DownTo 0);
  signal reset_from_host : std_logic;
  signal data_line_rx : std_logic;
  signal link_is_dead : std_logic;
  signal enabled : std_logic;
  signal host_address : std_logic_vector (23 DownTo 0);
  signal link_dir : integer;

begin
  
  link_controller : process ( clk, initialize, reset_in, h_target_label, link_data_in )

    variable link_state : link_state_t := SEND;
    variable link_clock_state : clock_state_t := state0;

    variable build_label_transmit_position : integer := 0;
    variable build_label_bit_position : integer := 0;

    procedure increment_label_positions is
    begin

      if ( build_label_bit_position > 0 ) then
        if ( build_label_transmit_position < 23 ) then
          build_label_transmit_position := build_label_transmit_position + 1;
          --report ("Adding to build_label_transmit_position: " & integer'image(build_label_transmit_position));
        else
          link_state := COMPLETE;
          link_complete <= '1';
        end if;
      end if;

      if ( build_label_bit_position < 23 ) then
        build_label_bit_position := build_label_bit_position + 1;
        --report ("Adding to build_label_bit_position: " & integer'image(build_label_bit_position));
      else
        link_state := COMPLETE;
      end if;

      b_position <= build_label_transmit_position;
      b_label_current <= build_label;

    end procedure increment_label_positions;

    procedure print_build_label ( print_label : std_logic_vector ) is
    begin

      report ("Printing BuildLabel:  " & std_logic'image(print_label(23))
                                       & std_logic'image(print_label(23))
                                       & std_logic'image(print_label(22))
                                       & std_logic'image(print_label(21))
                                       & std_logic'image(print_label(20))
                                       & std_logic'image(print_label(19))
                                       & std_logic'image(print_label(18))
                                       & std_logic'image(print_label(17))
                                       & std_logic'image(print_label(16))
                                       & std_logic'image(print_label(15))
                                       & std_logic'image(print_label(14))
                                       & std_logic'image(print_label(13))
                                       & std_logic'image(print_label(12))
                                       & std_logic'image(print_label(11))
                                       & std_logic'image(print_label(10))
                                       & std_logic'image(print_label(9))
                                       & std_logic'image(print_label(8))
                                       & std_logic'image(print_label(7))
                                       & std_logic'image(print_label(6))
                                       & std_logic'image(print_label(5))
                                       & std_logic'image(print_label(4))
                                       & std_logic'image(print_label(3))
                                       & std_logic'image(print_label(2))
                                       & std_logic'image(print_label(1))
                                       & std_logic'image(print_label(0)));

    end print_build_label;

  begin

    ------------------------------------------------------------
    -- 
    -- State Machine that Processes on Every Clock Cycle
    -- 
    ------------------------------------------------------------
    if ( clk'event and clk = '1' ) then

      -- only execute if link is enabled
      if ( enabled = '1' ) then

        case link_clock_state is
          when state0 =>

            -- set for reset if host requests a reset
            link_data_out <= reset_from_host;

            link_clock_state := state1;
          when state1 =>

            -- skipping read step from c++ design, because of the 
            -- parallel processing of the system
            -- set the reset out equal to the data line
            reset_out <= data_line_rx;

            link_clock_state := state2;
          when state2 =>

            -- check to see if a reset has occured and perform
            -- a link reset at this point
            if ( (data_line_rx  = '1') or (reset_from_host = '1') ) then

              --report ("Link Reset with New Target Label "
                      --& integer'image(to_integer(unsigned( target_label ) ) ) );
              
              link_state := SEND;
              build_label_bit_position := 0;
              build_label_transmit_position := 0;
              build_label <= "000000000000000000000000";
              link_complete <= '0';

              --report ( integer'image(to_integer(unsigned(host_address))) &
              --         " dir " & integer'image( link_dir ) & " reset to " &
              --         integer'image(to_integer(unsigned(target_label))));

            end if;

            link_clock_state := state3;
          when state3 =>

            -- clear all pieces of data used for reset to continue with
            -- our data communication
            link_data_out <= '0';
            reset_from_host <= '0';

            link_clock_state := state4;
          when state4 =>

            -- set data line if sending data
            if ( link_state = SEND ) then
              link_data_out <= target_label(23 - build_label_bit_position);
              --report ( integer'image(to_integer(unsigned(host_address))) &
              --         " dir " & integer'image( link_dir ) & " send bit position " &
              --         integer'image(23 - build_label_bit_position));
            end if;
              
            link_clock_state := state5;
          when state5 =>

            -- skipping read step from c++ design, because of the 
            -- parallel processing of the system
            -- check data line to see if we need to kill or change to RECIEVE
            if ( link_state = SEND ) then

              if ( data_line_rx = '1' ) then

                if ( build_label_bit_position > 0 ) then

                  if ( target_label(23 - build_label_bit_position) = '0' ) then



                    --report (integer'image(to_integer(unsigned(host_address))) & 
                    --        " dir " &
                    --        integer'image( link_dir ) &
                    --        " set to recieve ");

                    link_state := RECEIVE;

                  end if;

                    link_complete <= '0';

                else

                  --report( " killed ");

                  link_state := KILLED;
                  link_is_dead <= '1';
                  link_killed <= '1';
                  link_complete <= '1';

                end if;

              end if;

            end if;

            -- do nothing for recieving mode
            
            link_clock_state := state6;
          when state6 =>

            -- write to build label
            if( link_state = SEND or link_state = RECEIVE ) then

              build_label(23 - build_label_bit_position) <= data_line_rx;
              --report ( integer'image(to_integer(unsigned(host_address))) &
              --         " dir " &
              --         integer'image(link_dir) &
              --         " recieved " &
              --         std_logic'image( data_line_rx ) );

            end if;

            link_clock_state := state7;
          when state7 =>

            -- increment counter and reset the physical link
            increment_label_positions;
            
            link_data_out <= '0';

            -- uncomment to print label every iteration
            --print_build_label(build_label);

            link_clock_state := state8;
          when state8 =>

            link_clock_state := state0;

          when others =>

        end case;

      end if;

    end if;

    if ( initialize'event and initialize = '1' ) then

      link_data_out <= '0';
      link_complete <= '0';

      build_label_bit_position := 0;
      build_label_transmit_position := 0;
      build_label <= "000000000000000000000000";
      link_killed <= '0';
      link_is_dead <= '0';

    end if;

    if( h_target_label'event ) then

      if ( ( ( target_label(0) = '1' ) or ( target_label(0) = '0' ) ) and
      ( ( h_target_label(0) = '1' ) or ( h_target_label(0) = '0' ) ) ) then
        --report ("prev target label " & 
        --        integer'image(to_integer(unsigned(target_label))) &
        --        " new target label " &
        --        integer'image(to_integer(unsigned(h_target_label))) &
        --        " on controller " &
        --        integer'image(to_integer(unsigned(host_address))) &
        --        " in dir " &
        --        integer'image(link_dir));
      end if;

      target_label <= h_target_label;

      --report ("target label update");
      --print_build_label( h_target_label );

    end if;

    if ( link_data_in'event ) then

      --report ("Data Line Value Recieved" & std_logic'image(target_label(23)));

      data_line_rx <= link_data_in;

    end if;

    if ( reset_in'event ) then

      --report ("Resetting Link From Host" & std_logic'image(target_label(23)));

      reset_from_host <= reset_in;

    end if;

  end process link_controller;

  link_enabled : process ( enable )
  begin

    if (enable = '1' ) then
      --report ("Link Enabled");
    end if;
    if (enable = '0' ) then
      --report ("Link Disabled");
    end if;

    enabled <= enable;

  end process link_enabled;

  set_host : process ( h_name )
  begin

    host_address <= h_name;

  end process set_host;

  set_dir : process ( dir )
  begin

    link_dir <= dir;

  end process set_dir;

end bhv;
